/*
 * Copyright (c) 2018 LingoChamp Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.liulishuo.okdownload.sample;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.liulishuo.okdownload.DownloadTask;
import com.liulishuo.okdownload.SpeedCalculator;
import com.liulishuo.okdownload.StatusUtil;
import com.liulishuo.okdownload.core.ImageUtil;
import com.liulishuo.okdownload.core.LogUtillib;
import com.liulishuo.okdownload.core.TextUtils;
import com.liulishuo.okdownload.core.Util;
import com.liulishuo.okdownload.core.breakpoint.BlockInfo;
import com.liulishuo.okdownload.core.breakpoint.BreakpointInfo;
import com.liulishuo.okdownload.core.cause.EndCause;
import com.liulishuo.okdownload.core.listener.DownloadListener4WithSpeed;
import com.liulishuo.okdownload.core.listener.assist.Listener4SpeedAssistExtend;
import com.liulishuo.okdownload.sample.utils.DemoUtil;
import com.liulishuo.okdownload.sample.utils.LogUtil;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Image;
import ohos.agp.components.Slider;
import ohos.agp.components.Text;
import ohos.media.image.PixelMap;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;


/**
 * 单任务下载sample示例
 */
public class MainAbility extends Ability {

    private Slider slider;
    private Text statusTv;
    private Button actionTv;
    private static final String TAG = "SingleActivity";
    private DownloadTask task;
    private Image imageView;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);

        imageView = (Image) findComponentById(ResourceTable.Id_imageView);
        initComponent();
        initTask();
        initStatus(statusTv, slider);
        initAction(actionTv, statusTv, slider);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (task != null) {
            task.cancel();
        }
    }

    private void initComponent() {

        slider = (Slider) findComponentById(ResourceTable.Id_progressBar);
        statusTv = (Text) findComponentById(ResourceTable.Id_statusTv);
        actionTv = (Button) findComponentById(ResourceTable.Id_actionTv);
    }


    private void initTask() {
        final String filename = "single-test-" + System.currentTimeMillis() + ".apk";
        final String url = "https://cdn.llscdn.com/yy/files/xs8qmxn8-lls-LLS-5.8-800-20171207-111607.apk";
        final File parentFile = DemoUtil.getParentFile(this);
        task = new DownloadTask.Builder(url, parentFile)
                .setFilename(filename)
                // the minimal interval millisecond for callback progress
                .setMinIntervalMillisCallbackProcess(16)
                // ignore the same task has already completed in the past.
                .setAutoCallbackToUIThread(true)
                .setPassIfAlreadyCompleted(true)
                .build();
    }

    private void initStatus(Text statusTv, Slider progressBar) {
        final StatusUtil.Status status = StatusUtil.getStatus(task);
        if (status == StatusUtil.Status.COMPLETED) {
            progressBar.setProgressValue(progressBar.getMax());
        }

        statusTv.setText(status.toString());
        final BreakpointInfo info = StatusUtil.getCurrentInfo(task);
        if (info != null) {
            LogUtil.info(TAG, "init status with: " + info.toString());
            DemoUtil.calcProgressToView(progressBar, info.getTotalOffset(), info.getTotalLength());
        }
    }

    private String START = "START";
    private String Task_Start = "Task Start";
    private String Info_Ready = "Info Ready";

    private void initAction(final Text actionTv, final Text statusTv,
                            final Slider progressBar) {
        actionTv.setClickedListener(component -> {
            final boolean started = task.getTag() != null;
            if (started) {
                // to cancel
                task.cancel();
            } else {
                // to start
                startTask(statusTv, progressBar, actionTv);
                // mark
            }
        });
    }

    private void startTask(final Text statusTv, final Slider progressBar, final Text actionTv) {
        task.enqueue(new DownloadListener4WithSpeed() {
            private long totalLength;
            private String readableTotalLength;

            @Override
            public void taskStart(DownloadTask task) {
                LogUtil.info("zzhs", "startTask taskStart  " + task);
                statusTv.setText(Task_Start);
            }

            @Override
            public void infoReady(DownloadTask task, BreakpointInfo info, boolean fromBreakpoint, Listener4SpeedAssistExtend.Listener4SpeedModel model) {
                statusTv.setText(Info_Ready);
                totalLength = info.getTotalLength();
                readableTotalLength = Util.humanReadableBytes(totalLength, true);
                DemoUtil.calcProgressToView(progressBar, info.getTotalOffset(), totalLength);
            }

            @Override
            public void progressBlock(@NonNull DownloadTask task, int blockIndex, long currentBlockOffset, @NonNull SpeedCalculator blockSpeed) {
                LogUtil.info("zzhs", "progressBlock" + task.getInfo());
            }

            @Override
            public void connectStart(DownloadTask task, int blockIndex, Map<String, List<String>> requestHeaders) {
                final String status = "Connect Start " + blockIndex;
                statusTv.setText(status);
            }

            @Override
            public void connectEnd(DownloadTask task, int blockIndex, int responseCode, Map<String, List<String>> responseHeaders) {
                final String status = "Connect End " + blockIndex + "---" + responseCode + "---" + task.getFilename();
                statusTv.setText(status);
            }

            @Override
            public void progress(DownloadTask task, long currentOffset, SpeedCalculator taskSpeed) {
                taskProgressPrint(currentOffset, taskSpeed, progressBar, readableTotalLength, totalLength);
            }

            @Override
            public void blockEnd(DownloadTask task, int blockIndex, BlockInfo info, SpeedCalculator blockSpeed) {
            }

            @Override
            public void taskEnd(DownloadTask task, EndCause cause, @Nullable Exception realCause, SpeedCalculator taskSpeed) {
                taskEndPrint(task, cause, realCause, taskSpeed);
            }
        });
    }

    private void taskProgressPrint(long currentOffset, SpeedCalculator taskSpeed, Slider progressBar, String readableTotalLength, long totalLength) {
        final String readableOffset = Util.humanReadableBytes(currentOffset, true);
        final String progressStatus = readableOffset + "/" + readableTotalLength;
        final String speed = taskSpeed.speed();
        final String progressStatusWithSpeed = progressStatus + "(" + speed + ")";
        LogUtil.error("zzhs", "progress readableOffset:" + currentOffset + "  progressStatus:"
                + progressStatus + "  speed:" + speed + "  progressStatusWithSpeed:" + progressStatusWithSpeed);
        statusTv.setText(progressStatusWithSpeed);
        DemoUtil.calcProgressToView(progressBar, currentOffset, totalLength);
    }

    private void taskEndPrint(DownloadTask task, EndCause cause, @Nullable Exception realCause, SpeedCalculator taskSpeed) {
        final String statusWithSpeed = cause + "------" + taskSpeed.averageSpeed() + "------" + taskSpeed;
        statusTv.setText(statusWithSpeed);
        actionTv.setText(START);

        if (task.getFile().getName().endsWith("jpg") || task.getFile().getName().endsWith("jpeg") ||
                task.getFile().getName().endsWith("png")) {
            PixelMap pixelMap = ImageUtil.getPixelMapByFile(task.getFile());
            imageView.setPixelMap(pixelMap);
        }
        final String realMd5 = getFileSHA256(task.getFile());
        LogUtillib.e("zzhs_00699_", "SHA256-----" + realMd5);
        if (cause == EndCause.COMPLETED) {
            if (!TextUtils.isEmpty(realMd5) && "1fec0ea40673b1dd6a2a751686d5e0657786504aeebbc42a95b4b14467c845e7".endsWith(realMd5.toLowerCase(Locale.ROOT))) {
                LogUtillib.e("zzhs_00700_", "file download success!!!!");
            }
        }
    }

    private void printLogInfo(String tag, Map<String, List<String>> responseHeaderFields) {
        Iterator<Map.Entry<String, List<String>>> iterator = responseHeaderFields.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, List<String>> next = iterator.next();
            for (String temp : next.getValue()) {
                LogUtil.error(tag, next.getKey() + "---" + temp);
            }
        }
    }

    /**
     * 计算文件 SHA256
     *
     * @param file 文件
     * @return 返回文件的SHA256字符串，如果计算过程中任务的状态变为取消或暂停，返回null， 如果有其他异常，返回空字符串
     */
    private static String getFileSHA256(File file) {
        try (InputStream stream = Files.newInputStream(file.toPath(), StandardOpenOption.READ)) {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] buf = new byte[8192];
            int len;
            while ((len = stream.read(buf)) > 0) {
                digest.update(buf, 0, len);
            }
            return toHexString(digest.digest());
        } catch (IOException | NoSuchAlgorithmException e) {
            LogUtil.error("main", e.getMessage());
            return null;
        }
    }

    private static final char[] HEX_CODE = "0123456789ABCDEF".toCharArray();

    private static String toHexString(byte[] data) {
        StringBuilder string = new StringBuilder(data.length * 2);
        for (byte byt : data) {
            string.append(HEX_CODE[(byt >> 4) & 0xF]);
            string.append(HEX_CODE[(byt & 0xF)]);
        }
        return string.toString();
    }
}
